package cc.rengu.igas.channel.wechat.core.packswitch;

import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.DstChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.DstChannelCfg;
import cc.rengu.oltp.service.common.entity.DstChannelInfo;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.utility.util.RandomUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.util.List;
import java.util.Optional;

/**
 * 与微信实名验证通讯层报文解包前后、组前包后实现类
 * Created by 王成 on 2018/5/31.
 */
public class WechatMerchantClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {
    RgLogger rglog = RgLog.getLogger(this.getClass().getName());

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 获取数字签名字符串 */
        String jsonBefore = new String(bytes);
        rglog.debug("接收报文体:[{}]", jsonBefore);
        JSONObject jsonObject = JSON.parseObject(jsonBefore);
        if (jsonObject == null) {
            String url = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
            if (url.contains("cancel")) {
                rglog.info("撤销申请单，应答报文体为空");
                jsonObject = new JSONObject();
                jsonObject.put("return_code", WechatParamConstant.STRING_SUCCESS);
            } else {
                rglog.error("接收微信报文异常!");
                JSONObject json = new JSONObject();
                json.put("return_code", RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode());
                json.put("return_msg", RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
                json.put("err_code", RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode());
                json.put("err_code_des", RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
                return json.toString().getBytes("UTF-8");
            }
        } else {
            String signData = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGNATURE);
            String respTimeStamp = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_TIME_STAMP);
            String respNonceStr = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_NONCE);
            String serialNoRsp = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_CERT_SERIAL_NO);
            rglog.trace("获取应答证书序列号为:<{}>", serialNoRsp);

            /* 生成生成数字签名的字符串 */
            StringBuilder signBlock = new StringBuilder();
            signBlock.append(respTimeStamp).append("\n").append(respNonceStr).append("\n");
            if (jsonObject == null) {
                signBlock.append("\n");
            } else {
                signBlock.append(jsonBefore).append("\n");
            }
            rglog.debug("加签Block:<{}>", signBlock.toString());
            /* 获取微信验签密钥信息 */
            CertInfo certInfo = null;
            String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST);
            List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.V3_VERIFY_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            }
            if (certInfo == null) {
                rglog.error("根据证书索引获取证书失败[{}]");
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
            if (serialNoRsp != null && !serialNoRsp.equals(certInfo.getCertId())) {
                rglog.error("微信使用的平台证书序列号和当前所持有的微信支付平台证书的序列号不一致，请重新获取证书");
                JSONObject json = new JSONObject();
                json.put("return_code", RespCodeEnum.PUB_CERT_EXPIRED.getRespCode());
                json.put("return_msg", RespCodeEnum.PUB_CERT_EXPIRED.getRespDesc());
                json.put("err_code", RespCodeEnum.PUB_CERT_EXPIRED.getRespCode());
                json.put("err_code_des", RespCodeEnum.PUB_CERT_EXPIRED.getRespDesc());
                return json.toString().getBytes("UTF-8");
            }
            WechatSignService wechatSignService = new WechatSignServiceImpl();
            boolean verifyFlag = wechatSignService.verifySignString(signData, signBlock.toString(), certInfo);
            if (!verifyFlag) {
                rglog.error("验签失败!");
                jsonObject.put("return_code", RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                jsonObject.put("return_msg", RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                jsonObject.put("err_code", RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                jsonObject.put("err_code_des", RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                return jsonObject.toString().getBytes("UTF-8");
            }
            jsonObject.put("return_code", RespCodeEnum.TRANS_SUCCESS.getRespCode());
            jsonObject.put("return_msg", RespCodeEnum.TRANS_SUCCESS.getRespDesc());
            jsonObject.put("err_code", RespCodeEnum.TRANS_SUCCESS.getRespCode());
            jsonObject.put("err_code_des", RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        }
        return jsonObject.toJSONString().getBytes();
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        CertInfo certInfo = null;
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.V3_SIGN_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            }
        }
        if (certInfo == null) {
            rglog.error("获取服务商签名密钥失败");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.CERT_ID, certInfo.getCertDn());

        /**微信实名认证加签
         * 1、HTTP请求方法\n
         * 2、URL\n(获取请求的绝对URL，并去除域名部分得到参与签名的URL)
         * 3、请求时间戳\n
         * 4、请求随机串\n
         * 5、请求报文主体\n
         */
        String jsonBefore = new String(bytes);
        String url = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
        if (url != null && !url.isEmpty()) {
            url = url.substring(url.indexOf("/v3"), url.length());
        }
        String method = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_METHOD_NODE);
        String nonceStr = RandomUtil.randomString(WechatTreeNodeConstant.WX_RAND_STR_LEN);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RAND_STR, nonceStr);
        long timestamp = System.currentTimeMillis() / 1000;
        String breakFlag = "\n";
        StringBuilder sb = new StringBuilder();
        if ("GET".equals(method)) {
            sb.append(method).append(breakFlag).append(url).append(breakFlag).append(timestamp).append(breakFlag).append(nonceStr).append(breakFlag).append(breakFlag);
        } else {
            sb.append(method).append(breakFlag).append(url).append(breakFlag).append(timestamp).append(breakFlag).append(nonceStr).append(breakFlag).append(jsonBefore).append(breakFlag);
        }
        /* 生成生成数字签名的字符串 */
        rglog.debug("系统加签Block:<{}>", sb.toString());

        if (certInfo == null) {
            rglog.error("根据证书索引获取证书失败[{}]");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        WechatSignService wechatSignService = new WechatSignServiceImpl();
        String signData = wechatSignService.generateSignString(sb.toString(), certInfo);
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);
        /* 将数字签名设置到报文中 */
        String httpSignHead = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + dstChannelCfg.getProviderMchntNo() + "\",nonce_str=\"" + nonceStr + "\",signature=\"" + signData + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + certInfo.getCertDn() + "\"";
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_AUTHORIZATION, httpSignHead);
        if (null == bytes || 2 >= bytes.length) {
            return new byte[0];
        } else {
            return bytes;
        }
    }

}
